#region Usings

using System;
using System.Collections.Generic;
using System.Reflection;
using log4net;
using Karma.Framework.Core.IoC;
using Karma.Framework.Core.Services.Configuration;
using Karma.Framework.Core.Utils;

#endregion

namespace Karma.Framework.Core.Services.Invoker.Cache
{
    public class InvokerCache
    {
        private static IDictionary<string, MethodInfo> FactoryMethods = new Dictionary<string, MethodInfo>();
        private static ILog LOG = LogManager.GetLogger(typeof (InvokerCache).Name);

        private static IDictionary<string, ServiceReflectionInfo> Services =
            new Dictionary<string, ServiceReflectionInfo>();

        public static IEnumerable<string> ServiceNames
        {
            get { return Services.Keys; }
        }

        public static void Initialize()
        {
            if (LOG.IsDebugEnabled)
            {
                LOG.Debug("Starting initialization..");
            }
            InitializeServices();
            InitializeFactories();

            if (!LOG.IsDebugEnabled) return;

            LOG.Debug("Initialization finished.");
            LOG.Debug("Configured Factory Methods:");
            foreach (var info in FactoryMethods.Values)
            {
                LOG.Debug(string.Format("FactoryMethod: {0}", info));
            }
            LOG.Debug("Configured Services:");
            foreach (var key in Services.Keys)
            {
                var service = Services[key];
                LOG.Debug(string.Format("Service: {0}, Methods: {1}", key, service.Methods.Keys.ItemsToString()));
            }
        }

        private static void InitializeServices()
        {
            foreach (var key in ServiceInvokerConfiguration.Instance.Services.Keys)
            {
                var service = ServiceInvokerConfiguration.Instance.Services[key];
                var sri = new ServiceReflectionInfo
                              {
                                  ServiceType = service,
                                  Methods = new Dictionary<string, MethodReflectionInfo>()
                              };
                var methods = ReflectionUtils.GetMethods(service);
                foreach (var method in methods)
                {
                    if (!sri.Methods.ContainsKey(method.Name + method.GetParameters().Length))
                    {
                        sri.Methods.Add(method.Name + method.GetParameters().Length,
                                        new MethodReflectionInfo
                                            {
                                                Mi = method
                                            });
                    }
                }
                Services.Add(service.FullName, sri);
            }
        }

        private static void InitializeFactories()
        {
            var type = typeof (IoCFactory);
            var ioCFactoryResolveMethod = type.GetMethod("Resolve", new Type[] {});
            foreach (var key in ServiceInvokerConfiguration.Instance.Services.Keys)
            {
                var service = ServiceInvokerConfiguration.Instance.Services[key];
                FactoryMethods.Add(service.FullName, ioCFactoryResolveMethod.MakeGenericMethod(service));
            }
        }

        public static MethodInfo GetFactoryMethod(string service)
        {
            if (FactoryMethods.ContainsKey(service))
            {
                return FactoryMethods[service];
            }
            LOG.Error(string.Format("The service {0} is not configured.", service));
            throw new ArgumentException(string.Format("The service {0} is not configured.", service));
        }

        public static MethodInfo GetServiceMethod(string service, string method, object[] parameters)
        {
            if (Services.ContainsKey(service))
            {
                if (Services[service].Methods.ContainsKey(method + parameters.Length))
                {
                    return Services[service].Methods[method + parameters.Length].Mi;
                }
                LOG.Error(string.Format("The method [{0}.{1}] is not configured.", service, method + parameters.Length));
                throw new ArgumentException(string.Format("The method [{0}.{1}] is not configured.", service,
                                                          method + parameters.Length));
            }
            LOG.Error(string.Format("The service [{0}] is not configured.", service));
            throw new ArgumentException(string.Format("The service [{0}] is not configured.", service));
        }

        public static ServiceReflectionInfo GetServiceInfo(string service)
        {
            return Services[service];
        }
    }
}